/*
	GadgetShield.cpp
	Base class for shields
	(c)2000 Palestar, Richard Lyle
*/


#include "Debug/Trace.h"
#include "Debug/Assert.h"
#include "System/Messages.h"
#include "SceneryEffect.h"
#include "GadgetShield.h"
#include "GameContext.h"
#include "Resource.h"

//----------------------------------------------------------------------------

IMPLEMENT_ABSTRACT_FACTORY( GadgetShield, NounGadget ); 
REGISTER_FACTORY_KEY( GadgetShield, 4099634614070272882 );

BEGIN_ABSTRACT_PROPERTY_LIST( GadgetShield, NounGadget );
	ADD_TRANSMIT_UPDATE_PROPERTY( m_Charge );
END_PROPERTY_LIST();

//----------------------------------------------------------------------------

GadgetShield::GadgetShield()
{
	m_Flags |= FLAG_ACTIVE;
	m_Charge = 0;
	m_fModifier = 1.0f;
}

//----------------------------------------------------------------------------

void GadgetShield::setup()
{
	NounGadget::setup();
	m_nDamage = 0;
	m_Charge = maxCharge();
	m_Flags |= FLAG_ACTIVE;
}

void GadgetShield::initialize()
{
	NounGadget::initialize();
	m_Flags |= FLAG_ACTIVE;
}

//----------------------------------------------------------------------------

NounGadget::Type GadgetShield::type() const
{
	return SHIELD;
}

NounGadget::EnergyClass GadgetShield::energyClass() const
{
	return ENERGY_CLASS_DEFENSE;
}

dword GadgetShield::hotkey() const
{
	if ( (flags() & FLAG_MOUNT_FORE) == (flags() & FLAG_MOUNT_FULL) )
		return HK_NUMPAD8;
	else if ( (flags() & FLAG_MOUNT_RIGHT) == (flags() & FLAG_MOUNT_FULL) )
		return HK_NUMPAD6;
	else if ( (flags() & FLAG_MOUNT_LEFT) == (flags() & FLAG_MOUNT_FULL) )
		return HK_NUMPAD4;
	else if ( (flags() & FLAG_MOUNT_AFT) == (flags() & FLAG_MOUNT_FULL) )
		return HK_NUMPAD2;

	return HK_NUMPAD5;
}

CharString GadgetShield::status() const
{
	return CharString().format("%d%%", (m_Charge * 100) / maxCharge());
}

bool GadgetShield::canDamage( dword type ) const
{
	return false;
}

CharString GadgetShield::useTip( Noun * pTarget, bool shift ) const
{
	if ( shift )
	{
		if ( active() )
			return "Deactivate Shields";
		else
			return "Activate Shields";
	}

	return "Reinforce Shield";
}

bool GadgetShield::usable( Noun * pTarget, bool shift ) const
{
	if ( destroyed() )
		return false;
	return true;
}

bool GadgetShield::useActive() const
{
	return active();
}

int	lessChargeFirst( GadgetShield * pS1, GadgetShield * pS2 )
{
	return pS1->charge() - pS2->charge();	
}

void GadgetShield::use( dword when, Noun * pTarget, bool shift)
{
	// create use effect
	createUseEffect();
	if ( ! shift )
	{
		int needed = maxCharge() - m_Charge;
		if ( needed > 0 )
		{
			// suck energy from other shield facings into this one
			BaseNode * pParent = parent();
			if ( pParent != NULL )
			{
				Array<GadgetShield *> arShields;
				// get all available shield facings and sum up their energy
				int energypool = 0;
				for(int i=0;i<pParent->childCount();i++)
				{
					GadgetShield * pShield = WidgetCast<GadgetShield>( pParent->child(i) );
					if ( pShield == NULL || (pShield->mountFlags() & mountFlags()) != 0 )
						continue;		// ignore shields on the same facing

					arShields.push( pShield );
					energypool += pShield->m_Charge;
				}
					
				// sort them, those with lowest charge first
				arShields.qsort( lessChargeFirst );
				
				// each use should refill this shield facing by 10%, or up to maximum if less
				needed = Min<int>( needed, maxCharge() / 10.0f );
				int shieldCount = arShields.size();
				for( i = 0;i<shieldCount;i++)
				{
					GadgetShield * pShield = arShields[i];
					energypool -= pShield->m_Charge;				// this shield will be used and no longer add to the pool
					float equalpart = needed / ( shieldCount - i );	// every shield needs to give that much energy so the needed charge is reached
					float loadbalancer = 1.0f;						// have less than the others -> give less than the others
					if( i + 1 < shieldCount && needed < energypool )// isn't the last shield and have more than enough energy ?
					{	
						float averageenergy = energypool / ( shieldCount - ( i + 1 ) );	// average energy each shield remaining in the pool has
						ASSERT( pShield->m_Charge <= averageenergy );					// this array is sorted
						loadbalancer = pShield->m_Charge / averageenergy;	// only give a fair share, thus after giving energy to
																			// the same shield multiple times, all remaining shields
																			// will hold the same amount of energy
					}
					
					ASSERT( loadbalancer <= 1.0f );
					int suck = Min<int>( pShield->m_Charge, equalpart * loadbalancer );
					pShield->m_Charge = pShield->m_Charge - suck;
					// add to our charge
					m_Charge += suck;
					// subtract from the needed charge
					needed -= suck;
				}
			}
		}
	}
	else
	{
		// deactivate/activate shielding
		setFlags( FLAG_ACTIVE, !active() );
	}
}

int GadgetShield::useEnergy( dword nTick, int energy )
{
	if ( active() && energy >= energyCost() )
	{
		// subtract energy cost from shield charge
		energy -= energyCost();

		// charge the shield
		if ( m_Charge < maxCharge() && energy >= chargeEnergyCost() )
		{
			energy -= chargeEnergyCost();

			int charge = Min( maxCharge() - m_Charge, chargeRate() );
			m_Charge += charge;
		}
	}

	return energy;
}

//----------------------------------------------------------------------------

int GadgetShield::deflect( dword nType, int damage, Facing facing, const Vector3 & vPosition )
{
	if ( !active() || !canDeflect( nType ) )
		return damage;	// shield not active or doesn't deflect this type of damage
	if ( (facingMountFlags( facing ) & mountFlags()) == 0 )
		return damage;	// wrong facing

	float fMod = damageModifier( nType );
	if ( fMod <= 0.0f )
		return 0;		// full damage deflected!	

	int nModifiedDamage = fMod * damage;
	int nDeflected = Min( nModifiedDamage, m_Charge );
	m_Charge -= nDeflected;

	// show hit effect
	createHitEffect( vPosition );

	// divide the deflected damage amount by the modifier, to convert back into normal units
	return damage - (nDeflected / fMod);
}

static Vector3 FACING_VECTOR[] = 
{
	Vector3( 0, 0, 1 ),		// FRONT
	Vector3( 1, 0, 0 ),		// RIGHT
	Vector3( -1, 0, 0 ),	// LEFT
	Vector3( 0, 0, -1 ),	// BACK
	Vector3( 0, 1, 0),		// FULL
};

void GadgetShield::createHitEffect( const Vector3 & vDirection )
{
	// create effect client side only
	if ( context()->client() && parent() != NULL )
	{
		// show shield hit effect
		SceneryEffect * pHit = new SceneryEffect;
		pHit->setNounContext( new NounContext(hitEffect()) );
		pHit->setContext( context() );

		// set the frame
		pHit->setFrame( Matrix33( vDirection ) );
		// set the position
		pHit->setPosition( vDirection * parent()->radius() );
		// set the animation time
		pHit->setSegment( "Effect" );

		// attach this effect to our parent object
		parent()->attachNode( pHit );
		//context()->attachNoun( pHit, this );	// attach the hit effect to the ship
	}
}

void GadgetShield::setCharge( int charge )
{
	m_Charge = charge;
}

void GadgetShield::setModifier( float fModifier )
{
	m_fModifier = fModifier;
}

//----------------------------------------------------------------------------
// EOF
